#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <sys/types.h>
#include <sys/socket.h>
#include <netdb.h>
#include <unistd.h>
#include <fcntl.h>

#include <oci.h>

#include <glib.h>

#include "gkserver.h"
#include "loader.h"



#define	GKRELLM_VERSION_MAJOR	2
#define	GKRELLM_VERSION_MINOR	2
#define	GKRELLM_VERSION_REV	9
#define	GKRELLM_EXTRAVERSION	""

gboolean    verbose, debug;
gchar       *conf_file;

static GHashTable   *gkservers;

static GOptionEntry entries[] = 
{
  { "verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose, "Be verbose", NULL },
  { "debug", 'd', 0, G_OPTION_ARG_NONE, &debug, "Output debug messages", NULL },
  { "config_file", 'c', 0, G_OPTION_ARG_FILENAME, &conf_file, "Config file", NULL },
  { NULL }
};



static gchar
*get_line_after_tag(const gchar *buffer, const gchar *tag) 
{
  guint   x;
  gchar   **lines = g_strsplit_set(buffer, "\n", -1);
  gchar   *rv=NULL;

  for(x=0; lines[x]; x++)   
    if( !g_ascii_strncasecmp(lines[x], tag, strlen(tag)) )
      if( lines[x+1] )
	rv = g_strdup(lines[x+1]);
      
 

  g_strfreev(lines);
  return(rv);
}



static gboolean
read_gkserver_message(GIOChannel *ioc,
		      GIOCondition condition,
		      gpointer data)
{
  GKServer        *gkserver;
  gchar	          **server_lines;
  gsize           count;
  guint	          x;
  static gchar	  server_buf[4097], newmsg[4097];
  gint            fd = g_io_channel_unix_get_fd(ioc);

  g_io_channel_read_chars(ioc, server_buf, sizeof(server_buf), &count, (GError **) NULL);

  g_snprintf(newmsg, count+1, "%s", server_buf);

  if( (gkserver = g_hash_table_lookup(gkservers, &fd)) == NULL) {
    fprintf(stderr, "could not find gkserver\n");
    return;
  }

  //fprintf(stderr, "Message from %s : %d bytes\n", gkserver->hostname, count );


  server_lines = g_strsplit_set(newmsg, "\n", -1);
  
  for(x=0; server_lines[x]; x++) {

    if( !g_ascii_strncasecmp(server_lines[x], "<gkrellmd_setup>", strlen("<gkrellmd_setup>")) ) {
      gchar      *c_ncpu;
      
      c_ncpu = get_line_after_tag(newmsg, "<cpu_setup>");
      sscanf(c_ncpu, "n_cpus %d", &gkserver->ncpu);

      gkserver->gkversion = get_line_after_tag(newmsg, "<version>");
      gkserver->hostname  = get_line_after_tag(newmsg, "<hostname>");
      gkserver->sysname   = get_line_after_tag(newmsg, "<sysname>");
      g_free(c_ncpu);
    }

    if( !g_ascii_strncasecmp(server_lines[x], "</gkrellmd_setup>", strlen("</gkrellmd_setup>")) ) {
	oci_merge_host(gkserver);
    }


    if( !g_ascii_strncasecmp(server_lines[x], "<initial_update>", strlen("<initial_update>")) ) {
      gchar      *mem_line = get_line_after_tag(newmsg, "<mem>");

    }
      

    if( !g_ascii_strncasecmp(server_lines[x], "<cpu>", 5))
      oci_insert_cpu(gkserver, server_lines[x+1]);

    if( !g_ascii_strncasecmp(server_lines[x], "<mem>", 5)) 
      oci_insert_mem(gkserver, server_lines[x+1]);

    if( !g_ascii_strncasecmp(server_lines[x], "<proc>", 6)) 
      oci_insert_proc(gkserver, server_lines[x+1]);

  }

  g_strfreev(server_lines);
  return TRUE;
}



static gint
gkrellm_connect_to(const gchar *server, gint server_port)
{
  gint		   fd = -1;
  gint 		   rv = 0;
  struct addrinfo  hints, *res, *res0;
  gchar		   portnumber[6];


  snprintf (portnumber, sizeof(portnumber), "%d", server_port);
  memset(&hints, 0, sizeof(hints));
  hints.ai_family = PF_UNSPEC;
  hints.ai_socktype = SOCK_STREAM;
  hints.ai_protocol = 0;
  if ((rv = getaddrinfo(server, portnumber, &hints, &res0)) != 0)
    return -1;

  for (res = res0; res; res = res->ai_next) {

    if ((fd = socket(res->ai_family, res->ai_socktype, res->ai_protocol)) < 0)
      continue;

    if(verbose)
      printf("gkrellm_connect_to %s:%d ", server, server_port);
    
    if(connect(fd, res->ai_addr, res->ai_addrlen) >= 0)
      break;
    
    close(fd);
    fd = -1;
  }

  if (fd < 0) {
    if(verbose)
      printf(" unable to connect\n");
    return -1;
  }

  if(verbose)
    printf(" fd: %d\n", fd);

  return fd;
}



static gboolean
gkrellm_client_mode_connect(const gchar *host, gint port)
{
  gchar	      buf[128];
  GIOChannel  *io_channel;
  GKServer    *gkserver;
  gint        *client_fd = g_new0(gint, 1);

  *client_fd = gkrellm_connect_to(host, port);

  if (*client_fd < 0) {
    return FALSE;
  }

  gkserver = g_malloc(sizeof(GKServer));
  gkserver->client_fd  = *client_fd;
  gkserver->hostname   = g_strdup(host);
  gkserver->port       = port;

  g_hash_table_insert(gkservers, (gpointer) client_fd, (gpointer) gkserver);

  snprintf(buf, sizeof(buf), "gkrellm %d.%d.%d%s\n",
	   GKRELLM_VERSION_MAJOR, GKRELLM_VERSION_MINOR,
	   GKRELLM_VERSION_REV, GKRELLM_EXTRAVERSION);
  send(*client_fd, buf, strlen(buf), 0);

  fcntl(*client_fd, F_SETFL, O_NONBLOCK);

  io_channel = g_io_channel_unix_new(*client_fd);
  g_io_add_watch(io_channel, G_IO_IN | G_IO_PRI | G_IO_HUP, read_gkserver_message, (gpointer) NULL);

  return TRUE;
}




int
main (int argc, char *argv[])
{
  GMainLoop      *gmain;
  GOptionContext *context;
  GKeyFile       *keyfile;
  GError         *gerror=NULL;
  gchar          **conf_groups, *oracle_user, *oracle_pass, *oracle_sid;
  gsize          ngroups;
  guint          x;

  gkservers = g_hash_table_new(g_int_hash, g_int_equal);

  context = g_option_context_new ("- log gkrellmd messages to an Oracle database");
  g_option_context_add_main_entries (context, entries, NULL);
  g_option_context_parse (context, &argc, &argv, &gerror);
  if( !conf_file )
    conf_file = g_strdup("ogkrellm.conf");

  keyfile = g_key_file_new();
  if( g_key_file_load_from_file(keyfile, conf_file, G_KEY_FILE_NONE, &gerror) == FALSE) {
    fprintf(stderr, "Could not load config file '%s':  %s\n", conf_file, gerror->message);
    exit(1);
  }

  oracle_user = g_key_file_get_string(keyfile, "global", "oracle_user", (GError **) NULL);
  oracle_pass = g_key_file_get_string(keyfile, "global", "oracle_pass", (GError **) NULL);
  oracle_sid  = g_key_file_get_string(keyfile, "global", "oracle_sid", (GError **) NULL);

  if(!oracle_user || !oracle_pass || !oracle_sid) {
    fprintf(stderr, "Could not find required parameters in '%s'\n", conf_file);
    exit(2);
  }

  conf_groups = g_key_file_get_groups(keyfile, &ngroups);
  for(x=0; x < ngroups; x++) {
    gchar    *host;
    gint     port;

    if( !g_ascii_strncasecmp(conf_groups[x], "global", 6) )
      continue;

    host = g_key_file_get_string(keyfile, conf_groups[x], "host", (GError **) NULL);
    port = g_key_file_get_integer(keyfile, conf_groups[x], "port", (GError **) NULL);

    gkrellm_client_mode_connect(host, port);

    g_free(host);
  }


  if( oci_connect(oracle_user, oracle_pass, oracle_sid, 0) != OCI_SUCCESS)
    exit(3);

  gmain = g_main_loop_new((GMainContext *)NULL, FALSE);
  g_main_loop_run(gmain);

  return 0;
}

